package org.lwjgl.demo.device;

/**
 *  着色器程序类
 *
 * @author zhoudy
 * @date 2025/10/29 21:52
 */

import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Map;

import static org.lwjgl.opengl.GL30.*;

public class ShaderProgram {
    private final int programId;
    private final Map<String, Integer> uniforms;
    public ShaderProgram(String vertexShaderSource, String fragmentShaderSource) {
        System.out.println("开始创建着色器程序...");

        // 编译着色器
        System.out.println("编译顶点着色器...");
        int vertexShader = compileShader(vertexShaderSource, GL_VERTEX_SHADER, "Vertex Shader");

        System.out.println("编译片段着色器...");
        int fragmentShader = compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER, "Fragment Shader");

        // 创建程序
        System.out.println("创建着色器程序对象...");
        programId = glCreateProgram();
        if (programId == 0) {
            throw new RuntimeException("无法创建着色器程序对象");
        }
        uniforms = new HashMap<String, Integer>();

        // 附加着色器
        glAttachShader(programId, vertexShader);
        glAttachShader(programId, fragmentShader);

        // 链接程序
        System.out.println("链接着色器程序...");
        glLinkProgram(programId);

        // 检查链接状态
        checkLinkStatus();

        // 验证程序（可选，用于调试）
        // 移除验证步骤，避免警告
//        System.out.println("验证着色器程序...");
//        glValidateProgram(programId);
//        checkValidateStatus();

        // 清理着色器对象
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);

        System.out.println("✅ 着色器程序创建成功, ID: " + programId);
    }

    private int compileShader(String source, int type, String shaderName) {
        System.out.println("创建 " + shaderName + " 对象...");
        int shader = glCreateShader(type);
        if (shader == 0) {
            throw new RuntimeException("无法创建 " + shaderName + " 对象");
        }

        System.out.println("设置 " + shaderName + " 源代码...");
        glShaderSource(shader, source);

        System.out.println("编译 " + shaderName + "...");
        glCompileShader(shader);

        // 检查编译状态
        int[] success = new int[1];
        glGetShaderiv(shader, GL_COMPILE_STATUS, success);

        if (success[0] == 0) {
            String log = glGetShaderInfoLog(shader);
            String errorMsg = shaderName + " 编译失败:\n" +
                    "错误日志: " + log + "\n" +
                    "源代码:\n" + source;
            System.err.println(errorMsg);

            // 清理资源
            glDeleteShader(shader);
            throw new RuntimeException(errorMsg);
        }

        System.out.println("✅ " + shaderName + " 编译成功");
        return shader;
    }

    private void checkLinkStatus() {
        int[] success = new int[1];
        glGetProgramiv(programId, GL_LINK_STATUS, success);

        if (success[0] == 0) {
            String log = glGetProgramInfoLog(programId);
            String errorMsg = "着色器程序链接失败:\n" + log;
            System.err.println(errorMsg);

            // 清理资源
            glDeleteProgram(programId);
            throw new RuntimeException(errorMsg);
        }

        System.out.println("✅ 着色器程序链接成功");
    }

    private void checkValidateStatus() {
        int[] success = new int[1];
        glGetProgramiv(programId, GL_VALIDATE_STATUS, success);

        if (success[0] == 0) {
            String log = glGetProgramInfoLog(programId);
            System.err.println("着色器程序验证警告: " + log);
            // 注意：验证失败不一定是致命错误，所以只警告不抛出异常
        } else {
            System.out.println("✅ 着色器程序验证成功");
        }
    }

    public void use() {
        glUseProgram(programId);
    }

    public void setUniformMatrix4(String name, FloatBuffer matrix) {
        int location = glGetUniformLocation(programId, name);
        if (location == -1) {
            System.err.println("警告: 未找到uniform '" + name + "', 可能被优化掉了");
            return;
        }
        glUniformMatrix4fv(location, false, matrix);
    }

    // ==================== 原有方法保持不变 ====================
    public void setUniform(String name, float value) {
        int location = getUniformLocation(name);
        glUniform1f(location, value);
    }

    public void setUniform(String name, int value) {
        int location = getUniformLocation(name);
        glUniform1i(location, value);
    }

    // ==================== 新增：设置布尔值uniform ====================
    public void setUniform(String name, boolean value) {
        int location = getUniformLocation(name);
        glUniform1i(location, value ? 1 : 0);
    }

    // ==================== 新增：修复缺失的方法 ====================
    /**
     * 设置三维向量uniform (用于颜色、位置等)
     * 这个方法在Main.java中被调用
     */
    public void setUniform(String name, float x, float y, float z) {
        int location = getUniformLocation(name);
        glUniform3f(location, x, y, z);
    }


    // ==================== 新增：光照系统需要的Uniform方法 ====================
    /**
     * 设置三维向量uniform (用于颜色、位置等)
     */
    public void setUniform3f(String name, float x, float y, float z) {
        int location = getUniformLocation(name);
        glUniform3f(location, x, y, z);
    }

    /**
     * 设置光源结构体的属性 (如: light1.position, light1.ambient)
     */
    public void setLightUniform(String lightName, String property, float x, float y, float z) {
        String uniformName = lightName + "." + property;
        setUniform3f(uniformName, x, y, z);
    }

    /**
     * 设置材质结构体的属性 (如: material.shininess)
     */
    public void setMaterialUniform(String materialName, String property, float value) {
        String uniformName = materialName + "." + property;
        setUniform(uniformName, value);
    }

    /**
     * 设置材质纹理单元 (如: material.diffuse)
     */
    public void setMaterialTexture(String materialName, String property, int textureUnit) {
        String uniformName = materialName + "." + property;
        setUniform(uniformName, textureUnit);
    }

    private int getUniformLocation(String name) {
        if (!uniforms.containsKey(name)) {
            int location = glGetUniformLocation(programId, name);
            uniforms.put(name, location);
            if (location == -1) {
                System.err.println("警告: 未找到uniform '" + name + "'");
            } else {
                System.out.println("找到uniform: " + name + " (位置: " + location + ")");
            }
        }
        return uniforms.get(name);
    }

    public void cleanup() {
        if (programId != 0) {
            glDeleteProgram(programId);
            System.out.println("着色器程序已清理");
        }
    }

    // ==================== 新增：调试方法，打印所有uniform ====================
    public void printAllUniforms() {
        System.out.println("=== 着色器Uniform列表 ===");
        int[] count = new int[1];
        glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, count);

        for (int i = 0; i < count[0]; i++) {
            String name = glGetActiveUniform(programId, i, null, null);
            int location = glGetUniformLocation(programId, name);
            System.out.println("Uniform: " + name + " (位置: " + location + ")");
        }
        System.out.println("=======================");
    }
}